// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "SessionManager.hpp"
#include "Global.hpp"
#include "Exception.hpp"
#include "CallException.hpp"

using namespace ninf::client;
SessionManager :: SessionManager(int pid)
: parser(pid), lastID(-1)
{
}

void
SessionManager :: registerSession(Session * session){
  sessions[session->id] = session;
}


void 
SessionManager :: handle(LogEntry & entry){
  entry.print(stderr);

  if (entry.statusCode == NC_LOG_DONE){
    Global::global.executingJob --;
    int sessionID = idMap[entry.jobId0];
    Session * session = getSession(sessionID);
    session->recvData();
  } else
  if (entry.statusCode == NC_LOG_ABORT){
    Global::global.executingJob --;
    int sessionID = idMap[entry.jobId0];
    Session * session = getSession(sessionID);
    session->abort();
  }
}

void 
SessionManager :: throttling(){
  while (Global::global.executingJob > Global::global.maxExecuting){
    LogEntry entry = parser.parse();
    handle(entry);
  }
}


/* find the first "job submission" entry 
   and returns the JobID */
int 
SessionManager :: getSubmittedJobId(){
  while (true){
    LogEntry entry = parser.parse();
    handle(entry);
    
    if (entry.statusCode == NC_LOG_SUBMITTED){
      Global::global.executingJob ++;
      return entry.jobId0;
    }
  }
}


int 
SessionManager :: wait(Waiting & waiting){
		int doneID;
		for (int i = 0; i < waiting.len; i++){
				int sessionID = waiting.ids[i];
				if (getSession(sessionID)->status == NC_SESSION_DONE ||
						getSession(sessionID)->status == NC_SESSION_FAILED){
						waiting.sessionDone(sessionID);
						doneID = sessionID;
				}
		}
		while (!waiting.isDone()){
				LogEntry entry = parser.parse();
				// for debug print
				handle(entry);
				
				if (entry.statusCode == NC_LOG_DONE ||
						entry.statusCode == NC_LOG_ABORT) {
						int sessionID = idMap[entry.jobId0];
						waiting.sessionDone(sessionID);
						doneID = sessionID;
				}
		}
		
		if (waiting.getType() == NC_WAITING_OR){
				getSession(doneID)->status = NC_SESSION_REPORTED;
				lastID = doneID;
		} else {
				for (int i = 0; i < waiting.len; i++){
						int sessionID = waiting.ids[i];
						getSession(sessionID)->status = NC_SESSION_REPORTED;
						lastID = doneID;
				}
		}
		
		return doneID;
}

int
SessionManager :: wait(int sessionID){
		Waiting waiting(&sessionID, 1, NC_WAITING_AND);
		return wait(waiting);
}

int
SessionManager :: waitAll(){
		int * idList;
		int length = createUnreportedIDList(&idList);  
		Waiting waiting(idList, length, NC_WAITING_AND);  
		int tmp = wait(waiting);
		free(idList);
		return tmp;
}

int
SessionManager :: waitAny(){
		int * idList;
		int length = createUnreportedIDList(&idList);  
		Waiting waiting(idList, length, NC_WAITING_OR);  
		int tmp = wait(waiting);
		free(idList);
		return tmp;
}


int
SessionManager :: waitAnd(int * idList, int length){
		Waiting waiting(idList, length, NC_WAITING_AND);
		return wait(waiting);
}

int
SessionManager :: waitOr(int * idList, int length){
		Waiting waiting(idList, length, NC_WAITING_OR);
		return wait(waiting);
}


// allocate and returns session id list.
// be sure to free the list afterward.
int 
SessionManager :: createUnreportedIDList(int ** pIdList){
		int counter = 0;
		map<int, Session *>::const_iterator iter;
		for (iter=sessions.begin(); iter != sessions.end(); iter++) {
				if ((*iter).second->status != NC_SESSION_REPORTED)
						counter++;
		}
		*pIdList = (int *) malloc(sizeof(int) * counter);
		
		int i = 0;
		for (iter=sessions.begin(); iter != sessions.end(); iter++) {
				if ((*iter).second->status != NC_SESSION_REPORTED)
						*(*pIdList + i++) = (*iter).first;
		}
		return counter;
}

Session * 
SessionManager :: getSession(int sessionID){
		Session * s = sessions[sessionID];
		if (s == NULL)
				throw CallException("Session Not Found", __FILE__, __LINE__, 
															NINF_INVALID_SESSION_ID);
		return s;
}

void 
SessionManager :: registerMap(int sessionID, int condorID){
  fprintf(stderr, "idMap[%d] = %d\n", condorID, sessionID);
  idMap[condorID] = sessionID;
  revMap[sessionID] = condorID;
}

/*******************************************************/

void
SessionManager :: cancel(int sessionID){
  int condorID = revMap[sessionID];
  CondorSubmit::cancel(condorID);

}

void
SessionManager :: cancelAll(void){
  map<int, Session *>::const_iterator iter;
  for (iter=sessions.begin(); iter != sessions.end(); iter++) {
			if ((*iter).second->status == NC_SESSION_SUBMITTED) {
					int condorID = revMap[(*iter).second->id];
					CondorSubmit::cancel(condorID);
			}
  }
}

bool
SessionManager :: probe(int sessionID){
		SessionStatus stat = getSession(sessionID)->status;
		if (stat == NC_SESSION_DONE ||
				stat == NC_SESSION_FAILED ||
				stat == NC_SESSION_REPORTED) {
				getSession(sessionID)->status = NC_SESSION_REPORTED;
				return true;
		}
		return false;
}

bool
SessionManager :: probeOr(int *idList, int length , int * idP)  {
		int i; 
		for (i = 0; i < length; i++) {
				if (probe(idList[i])) { // found
						*idP = idList[i];
						return true;
				}
		}
		return false;
}

void
SessionManager :: perror(const char * str, FILE * fp){
  Session * session = getSession(lastID);
  if (session == NULL)
    EXCEPTION("cannot find last session");
  fprintf(fp, "%s: %s\n", str, NinfErrorCodeStrings[session->errorCode]);
}

void
SessionManager :: getFailedSessionID(int * idP) {
  map<int, Session *>::const_iterator iter;
  for (iter=sessions.begin(); iter != sessions.end(); iter++) {
			Session * session = (*iter).second;
			if (session->status == NC_SESSION_FAILED) {
					session->status = NC_SESSION_REPORTED;
					*idP = session->id;
					return;
			}
  }
	* idP = -1;
	return;
}
